import random
from django.core.paginator import Paginator
from django.db import transaction
from django.utils import timezone
from decimal import Decimal
from django import http
from django.shortcuts import render
from django.views import View
from django_redis import get_redis_connection
import json

from goods.models import SKU
from meiduo_mall.utlis.login_required import MyLoginRequiredview
from meiduo_mall.utlis.response_code import RET
from orders.models import OrderInfo, OrderGoods
from users.models import Address

#订单结算
class OrdersSettlementView(MyLoginRequiredview):
    def get(self,request):
        # 1,查询用户地址
        # Address.objects.filter(user_id=request.user.id).all()
        addresses=request.user.addresses.filter(is_deleted=False).all()#过滤没有被逻辑删除的所有地址

        #2.查询用户选中的商品
        user=request.user
        redis_conn=get_redis_connection('cart')
        cart_dict=redis_conn.hgetall("cart_%s"%user.id)
        selected_list=redis_conn.smembers("selected_%s"%user.id)

        # 3,数据拼接
        sku_list = []
        total_count = 0  # 总数量
        total_amount = 0  # 总价格
        for sku_id in selected_list:
            sku=SKU.objects.get(id=sku_id)
            sku_dict={
                "id":sku.id,
                "default_image_url":sku.default_image_url.url,
                "name":sku.name,
                "price":sku.price,
                "count":int(cart_dict[sku_id]),#通过sku_id取count
                "amount":str(sku.price * int(cart_dict[sku_id]))

            }
            sku_list.append(sku_dict)
            # 3,1累加商品数量,价格
            total_count += int(cart_dict[sku_id])
            total_amount += sku.price * int(cart_dict[sku_id])
        # 4,运费, 实付款
        freight= Decimal(10.0)#Decimal是decimal模块的函数,可以精确表示小数

        payment_amount=total_amount+freight

        # 携带数据,渲染页面
        context={
            "addresses":addresses,
            "skus":sku_list,
            "total_count":total_count,
            "total_amount":total_amount,
            "freight":freight,
            "payment_amount":payment_amount

        }
        return render(request, 'place_order.html', context=context)


#保存订单类视图,订单提交
class OrderCommitView(MyLoginRequiredview):
    @transaction.atomic  # 开启事物
    def post(self, request):
        #1.获取参数
        dict_data=json.loads(request.body.decode())
        address_id=dict_data.get("address_id")
        pay_method=dict_data.get("pay_method")


        # 2,校验参数
        # 2,1 为空校验
        if not all([address_id,pay_method]):
            return http.JsonResponse(status=400)

        # 2,2 地址是否存在
        try:
            address=Address.objects.get(id=address_id)
        except Exception as e:
            return http.JsonResponse(status=400)
        # 2,3 支付是否正确
        if pay_method not in [OrderInfo.PAY_METHODS_ENUM["CASH"],OrderInfo.PAY_METHODS_ENUM["ALIPAY"]]:
            return http.JsonResponse(status=400)

        # 3,数据入库
        # 3,1 订单编号
        user = request.user
        # timezone.now().strftime("%Y%m%d%H%m%s")获取订单生成时的时间,格式为字符串,年月日时分秒
        # "%06d%s"%(random.randint(0,999999),user.id): 取0~999999随机6位数,最后一位是客户id
        order_id= timezone.now().strftime("%Y%m%d%H%m%s")+"%06d%s"%(random.randint(0,999999),user.id)

        #3.2订单状态
        if pay_method==OrderInfo.PAY_METHODS_ENUM["CASH"]:
            status = OrderInfo.ORDER_STATUS_ENUM["UNSEND"]  # 货到付款
        else:
            status = OrderInfo.ORDER_STATUS_ENUM["UNPAID"]  # 待支付
        # TODO 在数据入库前设置保存点
        sid = transaction.savepoint()  # 设置保存点.保存事物开启到保存点的内容,一般保存点都设置在不会出问题的事物后面,当保存点后面代码有异常时,直接回滚到这个保存点.节省回滚时间
         # 3,3 创建订单信息对象,入库
        order_info =OrderInfo.objects.create(
            order_id=order_id,
            user=user,
            address=address,
            total_count=0,
            total_amount=Decimal(0.0),  # Decimal是精确到小数
            freight=Decimal(10.0),
            pay_method=pay_method,
            status=status,
            )
        #3.4获取要结算的商品,并入库
        redis_conn=get_redis_connection("cart")
        cart_dict=redis_conn.hgetall("cart_%s"%user.id)
        selected_list=redis_conn.smembers("selected_%s"%user.id)

        for sku_id in selected_list:
            while True:
                # 4,1 获取商品,数量
                sku=SKU.objects.get(id=sku_id)
                count=int(cart_dict[sku_id])

                #4.2判断库存
                if count>sku.stock:
                    # TODO 有问题就回滚到保存点
                    transaction.savepoint_rollback(sid)  # 库存不足就回滚,不让它往下走导致保存脏数据
                    return http.JsonResponse({"errmsg":"库存不足"})

                #TODO 模拟并发下单，效果是等候5秒钟在减库存量，让别人已原库存下单
                import time
                time.sleep(5)

                # # 4,3 减少库存,增加销量
                # sku.stock +=count
                # sku.sales +=count
                # sku.save()


                # TODO 使用乐观锁解决并发下单
                #4.3.1 准备新数据，老库存
                old_stock=sku.stock
                old_sales=sku.sales

                new_stock=old_stock-count
                new_sales=old_sales-count

                # 4,3,2 跟新数据,就是改之前再判断一次此时的库存是否与old_stock相同,相同才更新新的数据进去
                # 就是更新条件:1.id要与前端传来的一样 2.库存要与第一次查询的old_stock相同
                ret=SKU.objects.filter(id=sku_id,stock=old_stock).update(stock=new_stock,sales=new_sales)
                # update返回的不是一个对象,是影响的行数,是整数.即ret得到的是个整数,如果修改成功就返回修改了多少行.如果修改失败就返回0
                #4.3.3判断是否更新成功
                if ret==0:
                    continue #如果修改失败就结束这次循环，重新循环，一直循环到更新成功为止，


                # 4,4 创建订单商品对象,入库
                OrderGoods.objects.create(

                    order=order_info,
                    sku=sku,
                    count=count,
                    price=sku.price,

                )
                # 4,5 累加数量,价格
                order_info.total_count += count
                order_info.total_amount += (count * sku.price)

                break #以上都没问题就跳出循环
        # 5,提交订单信息
        transaction.savepoint_commit(sid)  # TODO 在前面都没问题时再提交事务
        order_info.save()

        # 6,清除redis数据
        redis_conn.hdel("cart_%s"%user.id,*selected_list)
        redis_conn.srem("selected_%s"%user.id,*selected_list)
        # 7,返回响应
        return http.JsonResponse({"code": RET.OK, "order_id": order_id})


#订单成功
class OrderSuccessView(View):
    def get(self,request):
        # 1,获取参数
        order_id=request.GET.get("order_id")
        payment_amount=request.GET.get("payment_amount")
        pay_method=request.GET.get("pay_method")

        # 2,拼接参数渲染页面
        context={
            "order_id":order_id,
            "payment_amount":payment_amount,
            "pay_method":pay_method
        }


        return render(request,'order_success.html',context)


#全部订单商品数据
class OrderInfoView(MyLoginRequiredview):
    def get(self,request,page_num):
        # 1,获取用户订单
        # orders = OrderInfo.objects.filter(user_id=request.user.id).order_by("-create_time").all()
        orders = request.user.orderinfo_set.order_by("-create_time").all()

        # 1,1 添加支付方式,订单状态显示信息
        for order in orders:
            order.paymethod_name = OrderInfo.PAY_METHOD_CHOICES[order.pay_method - 1][1]
            order.status_name = OrderInfo.ORDER_STATUS_CHOICES[order.status - 1][1]

        # 2,对订单进行分页
        paginate = Paginator(object_list=orders, per_page=3)
        page = paginate.page(page_num)
        order_list = page.object_list
        current_page = page.number
        total_page = paginate.num_pages

        # 2,拼接数据,渲染页面
        context = {
            "orders": order_list,
            "current_page": current_page,
            "total_page": total_page
        }
        return render(request, 'user_center_order.html', context=context)

#订单评论
class OrderCommetView(MyLoginRequiredview):
    #获取商品评论
    def get(self,request):

        # 接收订单编号
        order_id = request.GET.get('order_id')

        # 查询订单商品列表
        try:
            order = OrderInfo.objects.get(order_id=order_id, user_id=request.user.id)
        except:
            return http.JsonResponse(status=400)

        #获取所有已评价的订单信息
        order_dict=order.skus.filter(is_commented=False).all()
        # 获取订单的所有商品
        skus = []
        # detail表示OrderGoods类型的对象
        for detail in order_dict:
            skus.append({
                'sku_id': detail.sku.id,
                'default_image_url': detail.sku.default_image_url.url,
                'name': detail.sku.name,
                'price': str(detail.price),
                'order_id': order_id
            })

        context = {
            'skus': skus,

        }
        return render(request, 'goods_judge.html',context)


    def post(self,request):
        #1.获取数据
        dict_data=json.loads(request.body.decode())
        order_id=dict_data.get("order_id")
        sku_id=dict_data.get("sku_id")
        comment=dict_data.get("comment")
        score= dict_data.get("score")
        score = int(score)
        is_anonymous=dict_data.get("is_anonymous")

        #2.校验数据
        if not all([order_id,sku_id,comment,score]):
            return http.JsonResponse(status=400)

        try:
            sku = SKU.objects.get(id=sku_id)
            order = OrderInfo.objects.get(order_id=order_id)
        except Exception:
            return http.JsonResponse("111")

        # 3.数据入库

        OrderGoods.objects.filter(order=order,sku=sku).update(
            comment=comment,
            score=score,
            is_anonymous=is_anonymous,
            is_commented=True
        )
        OrderInfo.objects.filter(order_id=order_id).update(status=5)#变更订单状态

        count=sku.comments + 1
        SKU.objects.filter(id=sku_id).update(comments=count)
        return http.JsonResponse({"code":RET.OK})